package com.gzu.shop.interceptor;


import com.google.gson.Gson;
import com.gzu.shop.annotation.Access;
import com.gzu.shop.common.Result;
import com.gzu.shop.common.ResultStatus;
import com.gzu.shop.constant.JwtClaimsConstant;
import com.gzu.shop.constant.RoleConst;
import com.gzu.shop.context.BaseContext;
import com.gzu.shop.controller.OpLogController;
import com.gzu.shop.entity.OpLog;
import com.gzu.shop.enumeration.AccessLevel;
import com.gzu.shop.exception.LoginFailedException;
import com.gzu.shop.mapper.OpLogMapper;
import com.gzu.shop.properties.JwtProperties;
import com.gzu.shop.util.IpUtils;
import com.gzu.shop.util.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
//@AllArgsConstructor
public class JwtTokenUserInterceptor implements HandlerInterceptor {

    private final JwtProperties jwtProperties;
    private OpLogController opLogController;

    public JwtTokenUserInterceptor(JwtProperties jwtProperties, OpLogController opLogController) {
        this.jwtProperties = jwtProperties;
        this.opLogController = opLogController;
    }

    /**
     * 校验jwt
     *
     * @param request  拦截的请求
     * @param response 拦截的响应
     * @param handler  拦截器
     *   当请求过程中发生异常时，异
     *  常处理机制会将请求转发到错误处理路径（/error）

     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 记录访问时间，将其记录到请求头中
        request.setAttribute("_startTime", System.currentTimeMillis());

        // 判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            // 当前拦截到的不是动态方法，直接放行
            return true;
        }
        // 当前请求地址
        log.info("请求路径: {}", request.getRequestURI());


        // 1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getTokenName());

        log.info("token: {}", token);

        // Token为空，直接返回
        if (token == null) {
            return false;
        }

        // 2、校验令牌
        try {
            // 存用户基本信息
            Claims claims = JwtUtil.parseJWT(jwtProperties.getSecretKey(), token);
            BaseContext.setThreadLocal(claims);

            // 如果是管理员权限的
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取被拦截请求的方法
            Method method = handlerMethod.getMethod();
            // 获取被拦截的类
            Class<?> beanType = handlerMethod.getBeanType();
            // 通过反射获取类上的注解
            Access access = method.getAnnotation(Access.class);

            // 被拦截请求的类上有没有注解
            if (access == null) {
                access = beanType.getAnnotation(Access.class);
            }
            // 被拦截请求的方法上有没有注解，没有就放行
            if (access == null) {
                return true;
            }
            // 存在则进行处理
            if (access.value().getCode() == AccessLevel.ADMIN.getCode()) {
                System.out.println("##########################");
                System.out.println("此方法是管理员方法");
            }


            // 3、通过，放行
            return true;
        } catch (Exception ex) {
            // 4、不通过，响应401状态码
            log.error("Token过期、非法Token");

            response.setStatus(401);
            return false;
        }

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

//        log.info("_startTime:{}", request.getAttribute("_startTime"));

        // 记录log代码
        Long endTime = System.currentTimeMillis();
        Long startTime = (Long) request.getAttribute("_startTime");
        Long diff = (endTime - startTime);

        OpLog opLog = new OpLog();
        // 设置用户ID
        opLog.setUserId(String
                .valueOf(BaseContext.getThreadLocal().get(JwtClaimsConstant.USER_ID)));

        opLog.setReIp(IpUtils.getIpAddr(request));
        opLog.setReMethod(request.getMethod());
        opLog.setReUrl(request.getRequestURI());
        opLog.setReContent(request.getContentType());
        opLog.setReUa(request.getHeader(HttpHeaders.USER_AGENT));
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        opLog.setReTime(formatter.format(new Date()));
        opLog.setAccessTime(String.valueOf(diff));
        // 添加日志
        opLogController.addEntity(opLog);

//        log.info("oplog:{}", response.getContentType());
    }
}
